package check

import (
	"fmt"

	"gitee.com/u-language/u-language/ucom/ast"
	"gitee.com/u-language/u-language/ucom/astdata"
	"gitee.com/u-language/u-language/ucom/enum"
	"gitee.com/u-language/u-language/ucom/errcode"
	"gitee.com/u-language/u-language/ucom/internal/utils"
)

// ret_type_str 返回自动生成的表达式节点表达的类型
//   - ptr是被检查的节点]能为nil
//   - sbt是被检查的表达式所属代码块的符号表，不能为nil
//   - t是被检查节点所属的抽象语法树
//   - InAutoFree指示是否在自动释放块内
func ret_type_str(ptr ast.Expr, sbt *ast.Sbt, t *ast.Tree, InAutoFree bool) (code errcode.ErrCode, msg errcode.Msg, typestr string) {
	if ptr == nil { //用于支持无返回值函数
		return errcode.NoErr, nil, ""
	}
	defer check_recover(&code, &msg)
	switch n := ptr.(type) {
	case *ast.Object: //是对象节点
		code, typestr = retType(n, sbt)
		return code, msg, typestr
	case *ast.OpExpr: //是运算表达式节点
		code, msg, typestr = checkOpExpr(n, sbt, t, InAutoFree)
		if code != errcode.NoErr {
			return code, msg, ""
		}
	case *ast.CallNode: //是调用节点
		var ret []astdata.RetValue
		code, msg, ret = checkCall(n, sbt, t, InAutoFree)
		if code != errcode.NoErr {
			return code, msg, ""
		}
		if len(ret) == 0 { //如果函数没有返回值
			return errcode.FuncNoRetValue, errcode.NewMsgSymbol(n.FuncName.FuncName()), ""
		}
		return code, msg, ret[0].Type.Typ()
	case *ast.Objects: //如果是选择器
		var info ast.SymbolInfo
		code, msg, info, _, _, _ = check_selector(n, sbt)
		if code != errcode.NoErr {
			return code, msg, ""
		}
		return errcode.NoErr, nil, info.Type()
	case *ast.IndexExpr: //如果是索引表达式
		code, msg, typestr = ret_type_str(n.X, sbt, t, InAutoFree)
		if code != errcode.NoErr {
			return code, msg, ""
		}
		code, msg, _ = ret_type_str(n.Index, sbt, t, InAutoFree)
		if code != errcode.NoErr {
			return code, msg, ""
		}
		typestr, _, ok := utils.Elem(typestr)
		if !ok { //如果索引表达式元素类型错误
			return errcode.IndexExprElemTypeErr, nil, typestr
		}
		return errcode.NoErr, nil, typestr
	case *ast.Dereference: //如果是解引用
		code, msg, typestr = ret_type_str(n.Value, sbt, t, InAutoFree)
		if code != errcode.NoErr {
			return code, msg, ""
		}
		if typestr[0] != '&' {
			return errcode.OnlyTypePtrCanDeref, nil, ""
		}
		return errcode.NoErr, nil, typestr[1:]
	default:
		panic(fmt.Errorf("不支持的节点类型：%+v", ptr))
	}
	return
}

// ret_info 返回ptr在符号表记录的信息
//   - t是被检查节点所属的抽象语法树
//   - InAutoFree指示是否在自动释放块内
func ret_info(ptr ast.Node, sbt *ast.Sbt, t *ast.Tree, InAutoFree bool) (errcode.ErrCode, errcode.Msg, ast.SymbolInfo, bool, string) {
	switch ptr := ptr.(type) {
	case *ast.Object:
		info, bol := sbt.Have2(ptr.Name)
		typ := ast.RetType(ptr, sbt)
		return errcode.NoErr, nil, info, bol, typ
	case *ast.Objects:
		code, msg, info, is_local_decl, _, _ := check_selector(ptr, sbt)
		typ := ""
		if info.Kind != enum.NoSymbol {
			typ = info.Type()
		}
		return code, msg, info, is_local_decl, typ
	case *ast.IndexExpr: //是索引表达式
		code, msg, info, is_local_decl, typ := ret_info(ptr.X, sbt, t, InAutoFree)
		if code != errcode.NoErr {
			return code, msg, info, is_local_decl, typ
		}
		code, msg, _ = ret_type_str(ptr.Index, sbt, t, InAutoFree)
		if code != errcode.NoErr {
			return code, msg, info, is_local_decl, typ
		}
		typ, _, ok := utils.Elem(typ)
		if !ok { //如果索引表达式元素类型错误
			return errcode.IndexExprElemTypeErr, nil, info, is_local_decl, typ
		}
		return code, msg, info, is_local_decl, typ
	}
	panic(fmt.Errorf("未知的类型 %+v", ptr))
}

// retType获取Object的类型，对于未知的，将panic
//   - ptr是被获取类型的的Object,不能为nil
//   - sbt是Object所在区块的符号表,不能为nil
func retType(ptr *ast.Object, sbt *ast.Sbt) (errcode.ErrCode, string) {
	switch ptr.Kind {
	case ast.INTOBJ: //如果是int
		return errcode.NoErr, enum.Int
	case ast.FLOATOBJ: //如果是float
		return errcode.NoErr, enum.Float
	case ast.BoolObj: //如果是bool
		return errcode.NoErr, enum.Bool
	case ast.StringObj: //如果是字符串
		return errcode.NoErr, enum.String
	case ast.SymbolObj, ast.StructPtr | ast.SymbolObj: //如果是符号
		vv := sbt.Have(ptr.Name)
		return errcode.NoErr, vv.Type()
	case ast.NilObj: //如果是指针的零值
		return errcode.NoErr, enum.Nil
	case ast.LeaObj: //如果是取地址
		_, ok := ast.TypeEnumStrMap[ptr.Name]
		if ok {
			return errcode.NoErr, "&" + ptr.Name
		}
		vv := sbt.Have(ptr.Name)
		return errcode.NoErr, "&" + vv.Type()
	case ast.DerefObj: //如果是解引用
		vv := sbt.Have(ptr.Name)
		if vv.Kind != enum.SymbolConst && vv.Kind != enum.SymbolVar && vv.Kind != enum.SymbolNoParameVar { //如果既不是解引用变量，也不是解引用常量
			return errcode.UnrefNoVarAndConst, ""
		}
		typ := vv.Type()
		if typ[0] != '&' {
			return errcode.OnlyTypePtrCanDeref, ""
		}
		return errcode.NoErr, vv.Type()[1:]
	}
	panic(fmt.Errorf("未知的类型 ptr=%+v \n sbt=%+v", ptr, sbt))
}
